﻿//
// This file is part of AVSReflector project
// Copyright (C) 2011 Yuan Pengfei
//

using System;
using System.Collections.Generic;
using System.Text;

namespace AVSReflector.Core
{
    public class BitStream
    {
        byte[] content;
        int pos;

        public bool escape;

        public BitStream(byte[] c)
        {
            if (c.Length >= (1 << 28))
                throw new Exception("File is too large!");
            content = c;
            escape = false;
        }

        public uint Read(int n)
        {
            uint ret = 0;
            if (n > 32)
                throw new Exception("Can not read more than 32 bits!");
            // TODO: Make it efficient
            if (escape)
            {
                for(int i=0; i<n; ++i)
                {
                    if(pos % 8 == 6 && content[pos / 8] == 2 && content[pos / 8 - 1] == 0
                        && content[pos / 8 - 2] == 0)
                        pos += 2;
                    uint bit = content[pos / 8];
                    bit >>= (7 - pos % 8);
                    bit &= 1;
                    ret |= bit << (n - i - 1);
                    pos ++;
                }
            }
            else
            {
                for(int i=0; i<n; ++i)
                {
                    uint bit = content[pos / 8];
                    bit >>= (7 - pos % 8);
                    bit &= 1;
                    ret |= bit << (n - i - 1);
                    pos ++;
                }
            }
            return ret;
        }

        public uint Peek(int n)
        {
            uint ret = 0;
            if (n > 32)
                throw new Exception("Can not read more than 32 bits!");
            // TODO: Make it efficient
            if (escape)
            {
                int skip = 0;
                for (int i = 0; i < n; ++i)
                {
                    if ((pos + i + skip) % 8 == 6
                        && content[(pos + i + skip) / 8] == 2
                        && content[(pos + i + skip) / 8 - 1] == 0
                        && content[(pos + i + skip) / 8 - 2] == 0)
                        skip += 2;
                    uint bit = content[(pos + i + skip) / 8];
                    bit >>= (7 - (pos + i + skip) % 8);
                    bit &= 1;
                    ret |= bit << (n - i - 1);
                }
            }
            else
            {
                for (int i = 0; i < n; ++i)
                {
                    uint bit = content[(pos+i) / 8];
                    bit >>= (7 - (pos + i) % 8);
                    bit &= 1;
                    ret |= bit << (n - i - 1);
                }
            }
            return ret;
        }

        public bool EndOfStream()
        {
            return (pos >= 8 * content.Length);
        }

        public double Percentage()
        {
            double ret = pos / 8.0;
            ret /= content.Length;
            return ret;
        }

        public int Position()
        {
            return pos;
        }

        public void SkipToAlign()
        {
            if (pos % 8 != 0)
                pos += (8 - pos % 8);
        }

        public bool SkipToCode(uint code, int len)
        {
            SkipToAlign();
            uint t;
            while (true)
            {
                if (pos + len - 8 >= 8 * content.Length)
                {
                    pos = 8 * content.Length;
                    return false;
                }
                t = Peek(len);
                if (t == code)
                    break;
                Read(8);
            }
            return true;
        }

        public bool EndOfSlice()
        {
            if ((pos + 31 - pos % 8) / 8 >= content.Length)
            {
                pos = 8 * content.Length;
                return true;
            }
            return (Peek(32 - pos % 8) == ((1u << (31 - pos % 8)) | 1u));
        }
    }
}
